﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace General.Hierarchy
{
    /// <summary>
    /// Represents a single node within a tree.
    /// 
    /// when added to a Tree, the node becomes searchable from within the tree, and new nodes added/updated in the 
    /// tree will be reflected in the node.
    /// Changing the parent node value will update the children of the new parent.
    /// 
    /// The TreeNode does not need to be added to a tree to form a hierarchy. New nodes can be created and set as the 
    /// parent of any existing node.
    /// 
    /// The hierarchy enumerations on the node will navigate through the hierarchy generated by the linked nodes
    /// and their children, - these methods do not require the TreeNode to be in a Tree.
    /// 
    /// 
    /// 
    /// 
    /// </summary>
    /// <typeparam name="V">
    /// 
    /// </typeparam>
    [Serializable]
    public class TreeNode<V>
    {
        #region Fields

        /// <summary>
        /// the owning Tree
        /// </summary>
        protected Tree<V> _owner = null;

        /// <summary>
        /// the owning parent.
        /// </summary>
        protected TreeNode<V> _parent = null;

        /// <summary>
        /// local cache of child objects:
        /// </summary>
        private List<TreeNode<V>> _children = new List<TreeNode<V>>();

        /// <summary>
        /// handler for the parent object node added event.
        /// </summary>
        private Tree<V>.NodeEvent _nodeAddedHandler = null;

        #endregion

        #region Constructor

        /// <summary>
        /// default parameterless constructor.
        /// </summary>
        public TreeNode()
        {
            _nodeAddedHandler = new Tree<V>.NodeEvent(_owner_NodeAdded);
        }

        #endregion

        #region Properties

        /// <summary>
        /// owner property.
        /// </summary>
        public Tree<V> Owner { get { return _owner; } }

        /// <summary>
        /// parent property.
        /// </summary>
        public TreeNode<V> Parent
        {
            get { return _parent; }
            set
            {
                if (_parent == null)
                {
                    _parent = value;
                    if (!_parent._children.Contains(this))
                        _parent._children.Add(this);
                }
                else
                {
                    // change the parent of this node:
                    _parent._children.Remove(this);
                    _parent = value;
                    if (!_parent._children.Contains(this))
                        _parent._children.Add(this);
                }
            }

        }

        /// <summary>
        /// value property.
        /// </summary>
        public V Value { get; set; }

        /// <summary>
        /// the index within the parent tree collection.
        /// </summary>
        public int Index { get; set; }

        /// <summary>
        /// query the children of this node.
        /// </summary>
        public IEnumerable<TreeNode<V>> Children
        {
            get
            {
                return (from c in _children 
                        select c);
            }
        }

        /// <summary>
        /// query this nodes siblings.
        /// </summary>
        public IEnumerable<TreeNode<V>> Siblings
        {
            get
            {
                if (_parent == null)
                    yield break;
                else
                {
                    foreach (var sibling in _parent.Children)
                        if (sibling != this)
                            yield return sibling;
                }
            }
        }

        /// <summary>
        /// returns the parent of this node, it's parent and so on until the top of the tree is reached.
        /// </summary>
        public IEnumerable<TreeNode<V>> PathToTop 
        {
            get {
                var node = this._parent;
                while (node != null)
                {
                    yield return node;
                    node = node._parent;
                }
            }
        }

        /// <summary>
        /// returns all the descendents of the current node in hierarchy order.
        /// </summary>
        public IEnumerable<TreeNode<V>> Descendents
        {
            get
            {
                foreach (var node in _children)
                {
                    yield return node;
                    foreach (var descendent in node.Descendents)
                        yield return descendent;
                }
            }
        }

        /// <summary>
        /// hierarchy path for this node.
        /// </summary>
        public String Path
        {
            get
            {
                if (_parent == null)
                    return this.Value.ToString();
                else
                    return _parent.Path + "\\" + this.Value.ToString();
            }
        }

        #endregion

        #region Collection Modification Methods

        /// <summary>
        /// set the owning tree. the tree must already contain the node.
        /// </summary>
        /// <param name="owner"></param>
        public void SetOwner(Tree<V> owner)
        {
            if (_owner != null && _owner != owner)
                throw new ApplicationException("This node already belongs to a different tree!");

            if (!owner.Contains(this))
                throw new ApplicationException("Owner does not contain this node!");
            
            // set the owner and index:
            this._owner = owner;
            this.Index = owner.IndexOf(this);

            // attach an event-handler to the tree's node-added event to keep the children up to date.
            this._owner.NodeAdded += _nodeAddedHandler;

        }

        /// <summary>
        /// detach this node from it's owning tree.
        /// </summary>
        public void Detach()
        {
            RemoveFromTree(_owner);
        }

        /// <summary>
        /// remove this node, and all it's parents from the tree. the node cannot have children.
        /// </summary>
        /// <param name="owner"></param>
        public void RemoveFromTree(Tree<V> owner)
        {
            if (_children.Count > 0)
                throw new ApplicationException("Cannot remove a node with children!");
            if (_owner == null)
                throw new ApplicationException("This node does not have an owner to remove");

            if (Parent != null)
                Parent.RemoveFromTree(owner);

            if (_owner != null && _owner == owner)
            {
                _owner.Remove(this);
                _owner = null;
            }
        }

        /// <summary>
        /// add this node to the specified tree. the parent node must already be
        /// in the collection.
        /// </summary>
        /// <param name="tree"></param>
        public void AddToTree(Tree<V> tree)
        {
            if (_owner != null && _owner != tree)
                throw new ApplicationException("This node already belongs to a different tree!");

            if (!tree.Contains(this))
                 tree.Add(this);

        }

        /// <summary>
        /// update this nodes' internal list of children from the Tree's GetChildren method.
        /// </summary>
        public void UpdateChildren()
        {
            if (_owner != null)
            {
                _children.Clear();
                foreach (var node in _owner.GetChildren(this))
                {
                    _children.Add(node);
                }
            }
            else
                throw new ApplicationException("Tree node must have an Owner!");
        }

        #endregion

        #region Handlers

        /// <summary>
        /// Handle the "NodeAdded" event from the owning Tree object;
        /// if the added node is a parent to this node, add the new node to this node's children collection.
        /// </summary>
        /// <param name="sender">this will be the Tree control</param>
        /// <param name="node">
        /// the newly added node.
        /// </param>
        void _owner_NodeAdded(object sender, TreeNode<V> node)
        {
            if (node.Parent == this)
            {
                if (!_children.Contains(node))
                     _children.Add(node);
            }
        }

        #endregion

        /// <summary>
        /// a node is unique from it's index....
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            // generate the hash from the parent and value and index
            if (_parent != null)
            {
                return (_parent.Value.ToString() + Value.ToString()).GetHashCode() + Index;
            }
            else
                return Value.ToString().GetHashCode() + Index;
        }
    }
}
